#pragma once

#include "Type.h"
#include "Type.h"
#include "SubclassOf.h"
#include "Containers/Vector.h"
#include "Containers/Map.h"
#include "Containers/Optional.h"
#include "Containers/SharedObjectPtr.h"
#include "Containers/WeakObjectPtr.h"
#include "Containers/SharedInterfacePtr.h"
#include "Containers/ScriptDelegate.h"
#include "Widgets/WidgetAttribute.h"

#include "VectorType.h"
#include "MapType.h"
#include "ObjectPtr.h"
#include "WeakObjectPtr.h"
#include "SubclassOfType.h"
#include "OptionalType.h"
#include "DelegateType.h"
#include "FunctionType.h"
#include "WidgetAttributeType.h"
#include "PointerType.h"
#include "NativeOptional.h"


class CPointerGeneralized;

namespace WH
{
    template<typename T> 
    class TReflectionType
    {
    public:     

        static CType* FindOrCreateType() requires (std::is_same_v<T,std::remove_cvref_t<T>>)
        {
            return CGetType<T>()();
        }

        static CType* FindOrCreateType()  requires (!std::is_same_v<T,std::remove_cvref_t<T>>)
        {
            return TReflectionType<std::remove_cvref_t<T>>::FindOrCreateType();
        }
    };

    // template<typename ...T>
    // void FillTypeList(TVector<CType*>& TypeList);

    template<typename T>
    void FillTypeList(TVector<SDecoratedType>& TypeList)
    {
        TypeList.Add(SDecoratedType(TReflectionType<
                std::remove_cvref_t<T>
            >::FindOrCreateType()));
    }

    template<typename FirstT, typename ...T>
    requires (sizeof...(T) > 0)
    void FillTypeList(TVector<SDecoratedType>& TypeList)
    {
        FillTypeList<FirstT>(TypeList);
        FillTypeList<T...>(TypeList);
    }

    template<typename ...T>
    requires (sizeof...(T) == 0)
    void FillTypeList(TVector<SDecoratedType>& TypeList)
    {
    }

    WHENGINE_API CObjectPtrTypeGeneralized * FindOrCreateObjectPtrType(CClass* ValueType);

    template<typename T>  
    class TReflectionType<TSharedObjectPtr<T>>
    {
    public:
        static CType* FindOrCreateType()
        {
            CClass* Class = T::StaticClass();
            return FindOrCreateObjectPtrType(Class);
        }
    };

    WHENGINE_API CWeakObjectPtrTypeGeneralized* FindOrCreateWeakObjectPtrType(CClass* ValueType);

    template<typename T>  
    class TReflectionType<TWeakObjectPtr<T>>
    {
    public:
        static CType* FindOrCreateType()
        {
            CClass* Class = T::StaticClass();
            return FindOrCreateWeakObjectPtrType(Class);
        }
    };

    WHENGINE_API CType* FindOrCreateVectorType(CType* ValueType);

    template<typename T> 
    class TReflectionType<TVector<T>>
    {
    public:
        static CType* FindOrCreateType()
        {
            CType* Element = TReflectionType<T>::FindOrCreateType();
            return FindOrCreateVectorType(Element);
        }
    };    


    template<typename KeyType , typename ValueType> 
    class TReflectionType< TMap < KeyType,ValueType > >
    {
    public:
        static CType* FindOrCreateType()
        {
            CType* Key = TReflectionType<KeyType>::FindOrCreateType();
            CType* Value = TReflectionType<ValueType>::FindOrCreateType();

            auto Found= reinterpret_cast<CGenericInfo*>(CMapType::GetStaticType()->GenericInfo)
                ->GenericMap.Find({Key,Value});

            if(Found)
            {
                return *Found;
            }

            int64_t ValueOffset;
            {
                using TPairType=TPair<KeyType,ValueType>;
                ValueOffset=offsetof(TPairType,Value);
            }

            auto Ret=new CMapGeneralized(
                Key
                ,Value
                ,ValueOffset
                );
            return Ret;
        }
    };  

    template<typename T>
    class TReflectionType<TOptional<T>>
    { 
    public:
        static CType* FindOrCreateType()
        {
            auto Ret=new CNativeOptionalType(TReflectionType<T>::FindOrCreateType());      
            return Ret;
        }
    };


    template<typename T>
    class TReflectionType<TSubclassOf<T>>
    {
    public:
        static CType* FindOrCreateType()
        {
            CType* Element = T::StaticClass();

            auto Found= reinterpret_cast<CGenericInfo*>(CSubclassOfType::GetStaticType()->GenericInfo)
                ->GenericMap.Find({Element});

            if(Found)
            {
                return *Found;
            }

            auto Ret=new CSubclassOfGeneralized(Element);

            return Ret;
        }
           
    };

    WHENGINE_API CFunctionTypeGeneralized* FindOrCreateFunctionType(
        const SDecoratedType & ReturnType
        ,const TVector<SDecoratedType>& ParameterTypes);

    template<typename ReturnType,typename  ...ParameterType>
    class TReflectionType<ReturnType(ParameterType...)>
    {
    public:
        static CType* FindOrCreateType()
        {
            CType* ReturnValueType = TReflectionType<ReturnType>::FindOrCreateType();
            TVector<SDecoratedType> ParameterTypes;
            WH::FillTypeList<ParameterType...>(ParameterTypes);

            return WH::FindOrCreateFunctionType(ReturnValueType,ParameterTypes);
        }

    };

    WHENGINE_API CScriptDelegateGenerialized * FindOrCreateScriptDelegateType(CFunctionTypeGeneralized* FunctionType);

    template<typename ReturnType,typename  ...ParameterType>
    class TReflectionType<TScriptDelegate<ReturnType(ParameterType...)>>
    {
    public:
        static CType* FindOrCreateType()
        {
            CFunctionTypeGeneralized* FunctionType=
                reinterpret_cast<CFunctionTypeGeneralized*>(
                    TReflectionType<ReturnType(ParameterType...)>::FindOrCreateType()
                );
            return WH::FindOrCreateScriptDelegateType(FunctionType);
        }        
    };


    WHENGINE_API CType* FindOrCreateWidgetAttributeType(CType* ValueType);
    
    template<typename T>
    class TReflectionType<TWidgetAttribute<T>>
    {
    public:
        static CType* FindOrCreateType()
        {
            CType*  ValueType= TReflectionType<T>::FindOrCreateType();

            return WH::FindOrCreateWidgetAttributeType(ValueType);
        }
    };

    WHENGINE_API CPointerGeneralized* FindOrCreatePointerType(CType* ValueType);

    template<typename T>
    class TReflectionType<T*>
    {
    public:
        static CType* FindOrCreateType()
        {
            CType*  ValueType= TReflectionType<T>::FindOrCreateType();

            return WH::FindOrCreatePointerType(ValueType);
        }
    };


    template<typename T>
    class TReflectionType<TSharedInterfacePtr<T>>
    {
    public:
        static CType* FindOrCreateType()
        {
            return T::StaticInterface();
        }
    };
};